/**
 * Copyright 2008 Mathias Doenitz, http://lis.to/
 *
 * This file is part of the lis.to java desktop client. The lis.to java desktop client is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * The lis.to java desktop client is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with the lis.to java desktop client.
 * If not, see http://www.gnu.org/licenses/
 */

package listo.client;

import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import listo.client.dialogs.BaseDialog;
import listo.client.dialogs.PopupDialog;
import listo.client.model.Task;
import listo.client.model.Tasks;
import listo.client.model.operations.UpdateTasksOp;
import listo.utils.logging.Log;
import listo.utils.types.Cal;
import listo.utils.types.DateTime;

import javax.swing.*;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;

@Singleton
public class ReminderRunner implements Runnable, BaseDialog.OkCancelListener<PopupDialog.Model> {

    private final ContextManager contextManager;
    private final Provider<PopupDialog> popupDialogProvider;
    private final AppConfig appConfig;
    private final Preferences preferences;
    private final Log log;
    private final Provider<Formatter> formatterProvider;

    private final int pauseDelay;
    private final Object popupClosed = new Object();

    private Tasks tasks;
    private boolean pauseRequested;

    @Inject
    protected ReminderRunner(ContextManager contextManager, Provider<PopupDialog> popupDialogProvider,
                              AppConfig appConfig, Preferences preferences, Log log, Executors executors,
                              Provider<Formatter> formatterProvider) {
        this.contextManager = contextManager;
        this.popupDialogProvider = popupDialogProvider;
        this.appConfig = appConfig;
        this.preferences = preferences;
        this.log = log;
        this.formatterProvider = formatterProvider;

        pauseDelay = appConfig.getOnCancelReminderPause() * 60 * 1000;

        log.debug("Starting reminder runner");
        int delay = appConfig.getReminderCheck();
        executors.getReminderExecutor().scheduleWithFixedDelay(this, delay, delay, TimeUnit.SECONDS);
    }

    // run from the reminder thread
    public void run() {
        //log.trace("Running reminder check");

        DateTime now = new DateTime();
        tasks = new Tasks();
        for (Task task : contextManager.getCurrentContext().getTasks()) {
            if (task.isCompleted() || !task.hasFolders()) continue;
            DateTime due = task.getDue();
            if (due == null) continue;
            Integer reminder = task.getReminder();
            if (reminder == null) continue;
            DateTime popupTime = new Cal(due).add(Calendar.MINUTE, reminder).getDateTime();
            if (popupTime.before(now)) tasks.add(task);
        }
        if (tasks.isEmpty()) return;

        log.debug("%s due tasks found, opening reminder popup");

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                PopupDialog dialog = popupDialogProvider.get();
                dialog.setOkListener(ReminderRunner.this);
                dialog.run(new PopupDialog.Model(tasks, preferences.getDefaultSnoozeTime()));
            }
        });

        // we don't return from this task until the popup dialog is closed or the
        // application is shutting down, this way the reminder checking thread is
        // "suspended" until the user has taken some action
        try {
            synchronized (popupClosed) {
                popupClosed.wait();
            }

            // if the user cancelled the popup dialog we don't resume before the pause delay is up
            if (pauseRequested) Thread.sleep(pauseDelay);

        } catch (InterruptedException e) {
            // just quit
        }
    }

    // run from the EDT
    public void onOk(PopupDialog.Model model) {
        if (model.snoozeTime != null) {
            Formatter formatter = formatterProvider.get();

            // we need to set the snooze time on the open tasks
            for (Task task : tasks) {
                if (task.isCompleted() || !task.hasFolders()) continue;
                // we use the setReminderString() logic to set the snooze time
                task.setReminder(formatter.fromReminderString("++" + model.snoozeTime.toString(), task.getDue()));

                UpdateTasksOp op = new UpdateTasksOp();
                op.setTasks(task.getId());
                op.addField("reminder", Integer.toString(task.getReminder()));
                contextManager.addAndRun(op);
            }
        }
        // reactive reminder-checking
        pauseRequested = false;
        synchronized (popupClosed) {
            popupClosed.notify();
        }
    }

    // run from the EDT
    public void onCancel(PopupDialog.Model model) {
        log.debug("Reminder popup cancelled, disabling reminders for %s minutes", appConfig.getOnCancelReminderPause());

        // reactive reminder-checking requesting a pause
        pauseRequested = true;
        synchronized (popupClosed) {
            popupClosed.notify();
        }
    }

}
